package gang.org.springframework.framework.asm;

import gang.org.springframework.framework.core.type.classreading.GangSimpleMethodMetadata;
import gang.org.springframework.framework.metadata.GangMergedAnnotation;
import gang.org.springframework.framework.metadata.GangMergedAnnotations;
import gang.org.springframework.framework.type.GangMethodMetadata;
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * 业务意义
 * 1）ASM Class类字节码，方法解析
 * */
public class GangSimpleMethodMetadataReadingVisitor extends MethodVisitor {

    private final ClassLoader classLoader;
    private final String declaringClassName;
    private final int access;
    private final String name;
    private final String descriptor;
    private final List<GangMergedAnnotation<?>> annotations = new ArrayList<>();
    private final Consumer<GangSimpleMethodMetadata> consumer;

    private Source source;



    public GangSimpleMethodMetadataReadingVisitor(ClassLoader classLoader, String declaringClassName,
                                                  int access, String methodName, String descriptor,
                                                  Consumer<GangSimpleMethodMetadata> consumer) {
        super(Opcodes.ASM5);
        this.classLoader = classLoader;
        this.declaringClassName = declaringClassName;
        this.access = access;
        this.name = methodName;
        this.descriptor = descriptor;
        this.consumer = consumer;
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        return GangMergedAnnotationReadingVisitor.get(this.classLoader, this.getSource(), desc, visible, this.annotations::add);
    }

    @Override
    public void visitEnd() {
        if (!this.annotations.isEmpty()) {
            String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
            GangMergedAnnotations annotations = GangMergedAnnotations.of(this.annotations);
            GangSimpleMethodMetadata metadata = new GangSimpleMethodMetadata(this.name, this.access, this.declaringClassName, returnTypeName, annotations);
            this.consumer.accept(metadata);
        }
    }

    private Object getSource(){
        Source source = this.source;
        if (source == null){
            source = new Source(this.declaringClassName, this.name, this.descriptor);
            this.source = source;
        }
        return source;
    }

    static final class Source{
        private final String declaringClassName;
        private final String name;
        private final String descriptor;

        public Source(String declaringClassName, String name, String descriptor) {
            this.declaringClassName = declaringClassName;
            this.name = name;
            this.descriptor = descriptor;
        }
    }
}
